package com.ada.utils;

public class GenericTokenParser {
    private final String openToken;
    private final String closeToken;
    private final TokenHandler handler;

    /**
     * @param openToken  开始标记
     * @param closeToken 结束标记
     * @param handler    标记处理器
     */
    public GenericTokenParser(String openToken, String closeToken, TokenHandler handler) {
        this.openToken = openToken;
        this.closeToken = closeToken;
        this.handler = handler;
    }

    /**
     * 解析 ${} #{}
     *
     * @param text
     * @return 实现了配置文件、脚本等片段中占位符的解析处理工作，返回最终需要的数据
     * 解析工作用这个方法完成，处理工作由处理器handler的handleToken()方法实现
     */
    public String parse(String text) {
        //空字符串就返回
        if (text == null || text.isEmpty()) {
            return "";
        }
        //寻找开始标记，找不到说明不合格标准，直接返回
        int start = text.indexOf(openToken);
        if (start == -1) {
            return text;
        }
        //text转成字符数组，并且定义偏移量offset，builder最终需要返回字符串的变量builder
        //text中占位符对应的变量名为expression，如果start大于-1，说明有开始标记openToken，就继续执行
        char[] src = text.toCharArray();
        int offset = 0;
        final StringBuilder builder = new StringBuilder();
        StringBuilder expression = null;
        while (start > -1) {
            if (start > 0 && src[start - 1] == '\\') {
                // 排除语句转义字符操作
                // 开始标记前有转移字符，则不作为openToken进行处理，把openToken之前的字符移除反斜线加到builder里，并且更新偏移量到openToken的后一位
                // 这里的-1就是移除转义字符
                builder.append(src, offset, start - offset - 1).append(openToken);
                offset = start + openToken.length();
            } else {
                // 找到开始标记，寻找关闭标记
                // 重置expression
                if (expression == null) {
                    expression = new StringBuilder();
                } else {
                    expression.setLength(0);
                }
                //先把开始标记前的字符加到结果集合中，更新偏移量offset
                builder.append(src, offset, start - offset);
                offset = start + openToken.length();
                //从偏移量开始寻找结束标记
                int end = text.indexOf(closeToken, offset);
                while (end > -1) {
                    if (end > offset && src[end - 1] == '\\') {
                        // 排除语句转义字符操作
                        // 结束标记前有转义字符，把开始标记到结束标记中的内容，移除反斜线，放入expression
                        // 这里的-1就是移除转义字符
                        //更新偏移量，寻找closeToken
                        expression.append(src, offset, end - offset - 1).append(closeToken);
                        offset = end + closeToken.length();
                        end = text.indexOf(closeToken, offset);
                    } else {
                        //结束标记前没有转义，直接将中间的特殊参数变量值加入到expression中
                        expression.append(src, offset, end - offset);
                        break;
                    }
                }
                if (end == -1) {
                    // 没有找到结束标记，就把整句放入结果集
                    builder.append(src, start, src.length - start);
                    offset = src.length;
                } else {
                    //处理开始结束标记内的参数变量，加到结果集中
                    builder.append(handler.handleToken(expression.toString()));
                    offset = end + closeToken.length();
                }
            }
            //继续寻找下一个
            start = text.indexOf(openToken, offset);
        }
        //偏移量不到整句长度，直接把剩下的长度加上
        if (offset < src.length) {
            builder.append(src, offset, src.length - offset);
        }
        return builder.toString();
    }
}
